using System;
using System.Collections.Generic;
using System.Text;
using SlimDX;
using Barrage.Global;
using Barrage.Audios;

namespace Barrage.Elements
{
    /// <summary>
    /// Defines a camera used to render scenes from a specific point of view
    /// </summary>
    public abstract class Camera:Element
    {

        /// <summary>
        /// Camera projection matrix
        /// </summary>
        public Matrix Projection { get { return projection; } }

        /// <summary>
        /// Camera projection matrix
        /// </summary>
        protected Matrix projection = new Matrix();

        /// <summary>
        /// Camera position in world space coordinates
        /// </summary>
        public abstract Vector3 Position { get;}

        Plane[] frustumPlanes = new Plane[6];

        /// <summary>
        /// Checks if the sphere is inside the camera's frustum (usually used for frustum culling)
        /// </summary>
        /// <param name="sphere">Sphere to perform check</param>
        /// <param name="motion">Motion of the sphere</param>
        /// <returns>True if the sphere is outside the visible area, False otherwise</returns>
        public bool CheckCull(BoundingSphere sphere,Motion motion)
        {
            Vector4 trans = Vector3.Transform(sphere.Center,motion.WorldMatrix);
            
            return CheckCull(new BoundingSphere(new Vector3(trans.X,trans.Y,trans.Z), sphere.Radius * GameMath.VectorMaxValue(motion.Scale)));
        }
        /// <summary>
        /// Checks if the sphere is inside the camera's frustum (usually used for frustum culling)
        /// </summary>
        /// <param name="sphere">Sphere to perform check</param>
        /// <returns>True if the sphere is outside the visible area, False otherwise</returns>
        public bool CheckCull(BoundingSphere sphere)
        {
            Vector4 v=new Vector4(sphere.Center,1);
            for (int i = 0; i < 6; i++)
            {
                if (Plane.Dot(frustumPlanes[i], v) + sphere.Radius < 0)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// \internal Updates matrices and frustum planes
        /// </summary>
        protected override void DoUpdate()
        {
            base.DoUpdate();
            UpdateFrustum();
        }

        /// <summary>
        /// Updates frustum planes based on the projection matrix and the camera motion
        /// </summary>
        protected void UpdateFrustum()
        {
            Matrix vProj = Motion * Projection;
            
            frustumPlanes[0].Normal = new Vector3(vProj.M14 + vProj.M11, vProj.M24 + vProj.M21, vProj.M34 + vProj.M31);
            frustumPlanes[0].D = vProj.M44 + vProj.M41;

            frustumPlanes[1].Normal = new Vector3(vProj.M14 - vProj.M11, vProj.M24 - vProj.M21, vProj.M34 - vProj.M31);
            frustumPlanes[1].D = vProj.M44 - vProj.M41;

            frustumPlanes[2].Normal = new Vector3(vProj.M14 - vProj.M12, vProj.M24 - vProj.M22, vProj.M34 - vProj.M32);
            frustumPlanes[2].D = vProj.M44 - vProj.M42;

            frustumPlanes[3].Normal = new Vector3(vProj.M14 + vProj.M12, vProj.M24 + vProj.M22, vProj.M34 + vProj.M32);
            frustumPlanes[3].D = vProj.M44 + vProj.M42;

            frustumPlanes[4].Normal = new Vector3(vProj.M13, vProj.M23, vProj.M33);
            frustumPlanes[4].D = vProj.M43;

            frustumPlanes[5].Normal = new Vector3(vProj.M14 - vProj.M13, vProj.M24 - vProj.M23, vProj.M34 - vProj.M33);
            frustumPlanes[5].D = vProj.M44 - vProj.M43;

            for (int i = 0; i < 6; i++)
                frustumPlanes[i].Normalize();
        }

        /// <summary>
        /// \internal A camera has no geometry, so it does nothing in this call
        /// </summary>
        protected override void DoRender()
        {
            
        }
    }
}
